<!--  -->
<template>
    <view>
        <uni-popup ref="popup" :mask-click="false">
            <view class="toolbar">
                <view class="cancel" @tap="cancel"> 取消 </view>
                <view class="title">
                    {{ title }}
                </view>
                <view class="confirm" @tap="confirm"> 确定 </view>
            </view>
            <picker-view :indicator-style="indicatorStyle" :value="innerIndex" @change="bindChange" class="picker-view">
                <picker-view-column v-for="(item, index) in columns" :key="index">
                    <view class="item" v-for="(itemChild, indexChild) in item" :key="indexChild">
                        {{ itemChild }}
                    </view>
                </picker-view-column>
            </picker-view>
        </uni-popup>
    </view>
</template>
<script>
function sleep(value = 30) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve()
        }, value)
    })
}
function array(value) {
    if (typeof Array.isArray === "function") {
        return Array.isArray(value)
    }
    return Object.prototype.toString.call(value) === "[object Array]"
}
function deepClone(obj) {
    // 对常见的“非”值，直接返回原来值
    if ([null, undefined, NaN, false].includes(obj)) return obj
    if (typeof obj !== "object" && typeof obj !== "function") {
        // 原始类型直接返回
        return obj
    }
    const o = array(obj) ? [] : {}
    for (const i in obj) {
        if (obj.hasOwnProperty(i)) {
            o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]
        }
    }
    return o
}
export default {
    props: {
        // 是否展示picker弹窗
        show: {
            type: Boolean,
            default: false,
        },
        // 顶部标题
        title: {
            type: String,
            default: "",
        },
        // 对象数组，设置每一列的数据
        columns: {
            type: Array,
            default: () => [],
        },
    },
    data() {
        return {
            // 上一次选择的列索引
            lastIndex: [],
            // 索引值 ，对应picker-view的value
            innerIndex: [1, 1, 1],
            indicatorStyle: `height: 44px;`,
            innerColumns: [],
        }
    },
    mounted() {
        //
    },
    methods: {
        open() {
            this.$refs.popup.open("bottom")
        },
        close() {
            this.$refs.popup.close()
        },
        cancel() {
            this.$emit("cancel")
        },
        confirm() {
            this.$emit("confirm", {
                indexs: this.innerIndex,
                value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
                values: this.innerColumns,
            })
        },
        bindChange(e) {
            const { value } = e.detail
            let index = 0,
                columnIndex = 0
            // 通过对比前后两次的列索引，得出当前变化的是哪一列
            for (let i = 0; i < value.length; i++) {
                let item = value[i]
                if (item !== (this.lastIndex[i] || 0)) {
                    // 把undefined转为合法假值0
                    // 设置columnIndex为当前变化列的索引
                    columnIndex = i
                    // index则为变化列中的变化项的索引
                    index = item
                    break // 终止循环，即使少一次循环，也是性能的提升
                }
            }
            this.columnIndex = columnIndex
            const values = this.innerColumns
            // 将当前的各项变化索引，设置为"上一次"的索引变化值
            this.setLastIndex(value)
            this.setIndexs(value)

            this.$emit("change", {
                // #ifndef MP-WEIXIN
                // 微信小程序不能传递this，会因为循环引用而报错
                picker: this,
                // #endif
                value: this.innerColumns.map((item, index) => item[value[index]]),
                index,
                indexs: value,
                // values为当前变化列的数组内容
                values,
                columnIndex,
            })
        },
        // 设置index索引，此方法可被外部调用设置
        setIndexs(index, setLastIndex) {
            this.innerIndex = deepClone(index)
            if (setLastIndex) {
                this.setLastIndex(index)
            }
        },
        // 记录上一次的各列索引位置
        setLastIndex(index) {
            // 当能进入此方法，意味着当前设置的各列默认索引，即为“上一次”的选中值，需要记录，是因为changeHandler中
            // 需要拿前后的变化值进行对比，得出当前发生改变的是哪一列
            this.lastIndex = deepClone(index)
        },
        // 设置对应列选项的所有值
        setColumnValues(columnIndex, values) {
            // 替换innerColumns数组中columnIndex索引的值为values，使用的是数组的splice方法
            this.innerColumns.splice(columnIndex, 1, values)
            // 拷贝一份原有的innerIndex做临时变量，将大于当前变化列的所有的列的默认索引设置为0
            let tmpIndex = deepClone(this.innerIndex)
            for (let i = 0; i < this.innerColumns.length; i++) {
                if (i > this.columnIndex) {
                    tmpIndex[i] = 0
                }
            }
            // 一次性赋值，不能单个修改，否则无效
            this.setIndexs(tmpIndex)
        },
        // 获取对应列的所有选项
        getColumnValues(columnIndex) {
            // 进行同步阻塞，因为外部得到change事件之后，可能需要执行setColumnValues更新列的值
            // 索引如果在外部change的回调中调用getColumnValues的话，可能无法得到变更后的列值，这里进行一定延时，保证值的准确性
            ;(async () => {
                await sleep()
            })()
            return this.innerColumns[columnIndex]
        },
        // 设置整体各列的columns的值
        setColumns(columns) {
            this.innerColumns = deepClone(columns)
            // 如果在设置各列数据时，没有被设置默认的各列索引defaultIndex，那么用0去填充它，数组长度为列的数量
            if (this.innerIndex.length === 0) {
                this.innerIndex = new Array(columns.length).fill(0)
            }
        },
        // 获取各列选中值对应的索引
        getIndexs() {
            return this.innerIndex
        },
        // 获取各列选中的值
        getValues() {
            // 进行同步阻塞，因为外部得到change事件之后，可能需要执行setColumnValues更新列的值
            // 索引如果在外部change的回调中调用getValues的话，可能无法得到变更后的列值，这里进行一定延时，保证值的准确性
            ;(async () => {
                await sleep()
            })()
            return this.innerColumns.map((item, index) => item[this.innerIndex[index]])
        },
    },
    watch: {
        show(newValue, oldValue) {
            if (newValue === true) {
                this.open()
            } else {
                this.close()
            }
        },
        // 监听columns参数的变化
        columns: {
            immediate: true,
            handler(n) {
                this.setColumns(n)
            },
        },
    },
}
</script>
<style lang="scss" scoped>
.toolbar {
    display: flex;
    height: 42px;
    justify-content: space-between;
    align-items: center;
    background: #ffffff;
    border-radius: 24px 24px 0px 0px;
    border-bottom: #ebebeb solid 1px;
    .cancel {
        font-size: 15px;
        padding: 0 15px;
        color: $color-gray-2;
    }

    .title {
        flex: 1;
        text-align: center;
    }

    .confirm {
        font-size: 15px;
        padding: 0 15px;
        color: $color-main;
    }
}

.picker-view {
    width: 100%;
    height: 225px;
    background: #ffffff;
}

.item {
    height: 44px;
    line-height: 44px;
    align-items: center;
    justify-content: center;
    text-align: center;
}
</style>
